package com.nwpu.server.learning.controller;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.EscapeUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.servlet.ServletUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.nwpu.server.common.exception.BizCodeEnum;
import com.nwpu.server.common.response.R;
import com.nwpu.server.common.util.ErrorUtils;
import com.nwpu.server.common.util.FormatUtils;
import com.nwpu.server.common.util.TokenUtils;
import com.nwpu.server.common.util.file.FileTypeUtils;
import com.nwpu.server.common.util.file.FileUtils;
import com.nwpu.server.common.util.web.HttpUtils;
import com.nwpu.server.learning.annotation.OperationLogAspect;
import com.nwpu.server.learning.entity.dto.AlgorithmTaskDTO;
import com.nwpu.server.learning.entity.dto.RabbitMqResponseDTO;
import com.nwpu.server.learning.enums.StatusEnum;
import com.nwpu.server.learning.entity.FileEntity;
import com.nwpu.server.learning.entity.TaskEntity;
import com.nwpu.server.learning.service.FileService;
import com.nwpu.server.learning.service.TaskService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringEscapeUtils;
import org.springframework.amqp.AmqpException;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static com.nwpu.server.common.util.file.FileUtils.AppendWriteFile;

/**
 * @Author: YuXingZh
 * @Date: Created in 20:11 2022-02-13
 * @Description: 文件页
 */
@Slf4j
@RestController
@RequestMapping("learning/file")
public class FileController {

    @Value(value = "${learning.file.download.dir}")
    private String downloadDir;

    @Value(value = "${learning.file.user.graph}")
    private String graphDir;

    @Value(value = "${learning.file.user.feature}")
    private String featureDir;

    @Value(value = "${learning.file.user.label}")
    private String labelDir;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    private TaskService taskService;

    @Autowired
    private ThreadPoolTaskExecutor algorithmExecutor;

    /**
     * @param fileName 下载的文件编号
     * @param response
     * @功能描述 下载文件:
     */
    @GetMapping("/download")
    public R download(@RequestParam(value = "fileName") String fileName, HttpServletResponse response) {
        //判断文件夹是否存在，不存在时，创建文件夹
        File directoryFile = new File(downloadDir);
        if (!directoryFile.exists()) {
            //创建多个文件夹
            directoryFile.mkdirs();
        }

        File file = new File(downloadDir + "/" + fileName + ".zip");
        //判断文件是否为空，不为空时，保存文件
        if (FileUtil.isEmpty(file)) {
            return R.error(BizCodeEnum.USER_ERROR_A0700.getCode(), BizCodeEnum.USER_ERROR_A0700.getMsg());
        }

        response.setCharacterEncoding("UTF-8");
        ServletUtil.write(response, file);

        return R.ok();
    }

    /*
下载接口
 */
    @GetMapping("/fileDownload/{flag}")
    public void getFiles(@PathVariable String flag, HttpServletResponse response) {
        OutputStream os;//新建一个输出流对象
        String basePath = downloadDir; //定义文件上传的根路径
        List<String> fileNames = FileUtil.listFileNames(basePath);//获取所有的文件名称
        log.info("所有文件包括 : {}", JSONUtil.toJsonStr(fileNames));
        String fileName = fileNames.stream().filter(name -> name.contains(flag)).findAny().orElse("");//找到跟参数一致的文件
        try {
            if (StrUtil.isNotEmpty(fileName)) {
                response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
                response.setContentType("application/octet-stream");
                byte[] bytes = FileUtil.readBytes(basePath + fileName);//通过文件的路径读取文件字节流
                os = response.getOutputStream();//通过response的输出流返回文件
                os.write(bytes);
                os.flush();
                os.close();
            }
        } catch (Exception e) {
            System.out.println("文件下载失败");
        }

    }

    @RequestMapping("/httpUpload")
    public R httpUpload(@RequestParam("jsonParam") String jsonParam, @RequestPart(value = "graph", required = false) MultipartFile graph, @RequestPart(value = "feature", required = false) MultipartFile feature, @RequestPart(value = "label", required = false) MultipartFile label) {
        if (!graph.isEmpty()) {
            String fileName = graph.getOriginalFilename();  // 文件名
            log.info(fileName);
            File dest = new File(downloadDir + '/' + fileName);
            if (!dest.getParentFile().exists()) {
                dest.getParentFile().mkdirs();
            }
            try {
                graph.transferTo(dest);
            } catch (Exception e) {
                log.error("graph错误 : {}", e.getMessage());
                return R.error(BizCodeEnum.USER_ERROR_A0701.getCode(), BizCodeEnum.USER_ERROR_A0701.getMsg());
            }
        }

        if (!feature.isEmpty()) {
            String fileName = feature.getOriginalFilename();  // 文件名
            log.info(fileName);
            File dest = new File(featureDir + '/' + fileName);
            if (!dest.getParentFile().exists()) {
                dest.getParentFile().mkdirs();
            }
            try {
                feature.transferTo(dest);
            } catch (Exception e) {
                log.error("graph错误 : {}", e.getMessage());
                return R.error(BizCodeEnum.USER_ERROR_A0701.getCode(), BizCodeEnum.USER_ERROR_A0701.getMsg());
            }
        }
        if (!label.isEmpty()) {

            String fileName = label.getOriginalFilename();  // 文件名
            log.info(fileName);
            File dest = new File(labelDir + '/' + fileName);
            if (!dest.getParentFile().exists()) {
                dest.getParentFile().mkdirs();
            }
            try {
                label.transferTo(dest);
            } catch (Exception e) {
                log.error("graph错误 : {}", e.getMessage());
                return R.error(BizCodeEnum.USER_ERROR_A0701.getCode(), BizCodeEnum.USER_ERROR_A0701.getMsg());
            }

        }

        // 文件名
        String fileId = FormatUtils.getUUID();

        // 去除转义
        AlgorithmTaskDTO algorithmTaskDTO = JSONUtil.toBean(jsonParam, AlgorithmTaskDTO.class);
        algorithmTaskDTO.setTaskId(fileId);

        try {
            converMessage(algorithmTaskDTO);
        } catch (AmqpException amqpException) {
            log.error("发送消息失败:{}", ErrorUtils.errorInfoToString(amqpException));
            return R.error(BizCodeEnum.SERVICE_ERROR_C0121.getCode(), BizCodeEnum.SERVICE_ERROR_C0121.getMsg());
        }

        Date date = DateUtil.date();

        // 写入任务
        log.info("写入任务");
        TaskEntity taskEntity = new TaskEntity();
        taskEntity.setCreateTime(date);
        taskEntity.setId(fileId);
        taskEntity.setStatus(StatusEnum.NEW.getValue());
        taskEntity.setUserId("1");
        taskEntity.setUsername("yuxingzh");
        taskEntity.setTaskSn(algorithmTaskDTO.getTaskType());
        taskService.save(taskEntity);

        return R.ok().put("uuid", fileId);
    }

    @RequestMapping("/fdownload")
    public String fileDownLoad(HttpServletResponse response, @RequestParam("fileName") String fileName) {
        File file = new File(downloadDir + '/' + fileName);
        if (!file.exists()) {
            return "下载文件不存在";
        }
        response.reset();
        response.setContentType("application/octet-stream");
        response.setCharacterEncoding("utf-8");
        response.setContentLength((int) file.length());
        response.setHeader("Content-Disposition", "attachment;filename=" + fileName);

        try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));) {
            byte[] buff = new byte[1024];
            OutputStream os = response.getOutputStream();
            int i = 0;
            while ((i = bis.read(buff)) != -1) {
                os.write(buff, 0, i);
                os.flush();
            }
        } catch (IOException e) {
            log.error("{}", e);
            return "下载失败";
        }
        return "下载成功";
    }

    @PostMapping("/uploadFiles")
    public R uploadFile(@RequestParam("files") MultipartFile[] files) {
        // 判断文件数组长度
        if (files.length <= 0) {
            return R.error(BizCodeEnum.USER_ERROR_A0701.getCode(), BizCodeEnum.USER_ERROR_A0701.getMsg());
        }
        String fileId = FormatUtils.getUUID();

        for (MultipartFile file : files) {
            // 源文件名
            String originalFilename = file.getOriginalFilename();
            // 文件格式
            String suffix = originalFilename.substring(originalFilename.lastIndexOf("."));
            // 新文件名，避免文件名重复，造成文件替换问题
            String fileName = fileId + "." + suffix;
            // 文件存储路径
            String filePath = downloadDir + '/';
            // 文件全路径
            File targetFile = new File(filePath + fileName);

            // 判断文件存储目录是否存在，不存在则新建目录
            if (!targetFile.getParentFile().exists()) {
                targetFile.getParentFile().mkdir();
            }
            try {
                // 将图片保存
                file.transferTo(targetFile);
            } catch (IOException e) {
                log.info("文件上传异常={}", e);
            }
        }
        log.info("上传成功");
        return R.ok().put("uuid", fileId);
    }


    @PostMapping("/uploadFiles1")
    public R uploadFile1(@RequestParam("graph") MultipartFile graph, @RequestParam("feature") MultipartFile feature, @RequestParam("label") MultipartFile label) {

        String fileId = FormatUtils.getUUID();

        if (!graph.isEmpty()) {
            // 源文件名
            String originalFilename = graph.getOriginalFilename();
            // 文件格式
            String suffix = originalFilename.substring(originalFilename.lastIndexOf("."));
            // 新文件名，避免文件名重复，造成文件替换问题
            String fileName = fileId + "." + suffix;
            // 文件存储路径
            String filePath = graphDir + '/';
            // 文件全路径
            File targetFile = new File(filePath + fileName);

            // 判断文件存储目录是否存在，不存在则新建目录
            if (!targetFile.getParentFile().exists()) {
                targetFile.getParentFile().mkdir();
            }
            try {
                // 将图片保存
                graph.transferTo(targetFile);
            } catch (IOException e) {
                log.info("文件上传异常={}", e);
            }
        }

        if (!feature.isEmpty()) {
            // 源文件名
            String originalFilename = feature.getOriginalFilename();
            // 文件格式
            String suffix = originalFilename.substring(originalFilename.lastIndexOf("."));
            // 新文件名，避免文件名重复，造成文件替换问题
            String fileName = fileId + "." + suffix;
            // 文件存储路径
            String filePath = featureDir + '/';
            // 文件全路径
            File targetFile = new File(filePath + fileName);

            // 判断文件存储目录是否存在，不存在则新建目录
            if (!targetFile.getParentFile().exists()) {
                targetFile.getParentFile().mkdir();
            }
            try {
                // 将图片保存
                feature.transferTo(targetFile);
            } catch (IOException e) {
                log.info("文件上传异常={}", e);
            }
        }

        if (!label.isEmpty()) {
            // 源文件名
            String originalFilename = label.getOriginalFilename();
            // 文件格式
            String suffix = originalFilename.substring(originalFilename.lastIndexOf("."));
            // 新文件名，避免文件名重复，造成文件替换问题
            String fileName = fileId + "." + suffix;
            // 文件存储路径
            String filePath = labelDir + '/';
            // 文件全路径
            File targetFile = new File(filePath + fileName);

            // 判断文件存储目录是否存在，不存在则新建目录
            if (!targetFile.getParentFile().exists()) {
                targetFile.getParentFile().mkdir();
            }
            try {
                // 将图片保存
                label.transferTo(targetFile);
            } catch (IOException e) {
                log.info("文件上传异常={}", e);
            }
        }
        log.info("上传成功");
        return R.ok().put("uuid", fileId);
    }


    @PostMapping("/uploadFiles2")
    public R uploadFile2(@RequestParam("jsonParam") String jsonParam, @RequestParam(value = "email", defaultValue = "") String email, @RequestParam("graph") MultipartFile graph, @RequestParam("feature") MultipartFile feature, @RequestParam("label") MultipartFile label) {
        log.info("jsonParam : {} , email : {}", jsonParam, email);
        String fileId = FormatUtils.getUUID();

        if (!graph.isEmpty()) {
            // 源文件名
            String originalFilename = graph.getOriginalFilename();
            // 文件格式
            String suffix = originalFilename.substring(originalFilename.lastIndexOf("."));
            // 新文件名，避免文件名重复，造成文件替换问题
            String fileName = fileId + "." + suffix;
            // 文件存储路径
            String filePath = graphDir + '/';
            // 文件全路径
            File targetFile = new File(filePath + fileName);

            // 判断文件存储目录是否存在，不存在则新建目录
            if (!targetFile.getParentFile().exists()) {
                targetFile.getParentFile().mkdir();
            }
            try {
                // 将图片保存
                graph.transferTo(targetFile);
            } catch (IOException e) {
                log.info("文件上传异常={}", e);
            }
        }

        if (!feature.isEmpty()) {
            // 源文件名
            String originalFilename = feature.getOriginalFilename();
            // 文件格式
            String suffix = originalFilename.substring(originalFilename.lastIndexOf("."));
            // 新文件名，避免文件名重复，造成文件替换问题
            String fileName = fileId + "." + suffix;
            // 文件存储路径
            String filePath = featureDir + '/';
            // 文件全路径
            File targetFile = new File(filePath + fileName);

            // 判断文件存储目录是否存在，不存在则新建目录
            if (!targetFile.getParentFile().exists()) {
                targetFile.getParentFile().mkdir();
            }
            try {
                // 将图片保存
                feature.transferTo(targetFile);
            } catch (IOException e) {
                log.info("文件上传异常={}", e);
            }
        }

        if (!label.isEmpty()) {
            // 源文件名
            String originalFilename = label.getOriginalFilename();
            // 文件格式
            String suffix = originalFilename.substring(originalFilename.lastIndexOf("."));
            // 新文件名，避免文件名重复，造成文件替换问题
            String fileName = fileId + "." + suffix;
            // 文件存储路径
            String filePath = labelDir + '/';
            // 文件全路径
            File targetFile = new File(filePath + fileName);

            // 判断文件存储目录是否存在，不存在则新建目录
            if (!targetFile.getParentFile().exists()) {
                targetFile.getParentFile().mkdir();
            }
            try {
                // 将图片保存
                label.transferTo(targetFile);
            } catch (IOException e) {
                log.info("文件上传异常={}", e);
            }
        }

        // 去除转义
        AlgorithmTaskDTO algorithmTaskDTO = JSONUtil.toBean(jsonParam, AlgorithmTaskDTO.class);
        algorithmTaskDTO.setTaskId(fileId);

        try {
            converMessage(algorithmTaskDTO);
        } catch (AmqpException amqpException) {
            log.error("发送消息失败:{}", ErrorUtils.errorInfoToString(amqpException));
            return R.error(BizCodeEnum.SERVICE_ERROR_C0121.getCode(), BizCodeEnum.SERVICE_ERROR_C0121.getMsg());
        }

        Date date = DateUtil.date();

        // 写入任务
        log.info("写入任务");
        TaskEntity taskEntity = new TaskEntity();
        taskEntity.setCreateTime(date);
        taskEntity.setId(fileId);
        taskEntity.setStatus(StatusEnum.NEW.getValue());
        if (StrUtil.isEmpty(email)) {
            taskEntity.setUserId("1");
        } else {
            taskEntity.setUserId(email);
        }
        taskEntity.setUsername("yuxingzh");
        taskEntity.setTaskSn(algorithmTaskDTO.getTaskType());
        taskService.save(taskEntity);

        log.info("上传成功");
        return R.ok().put("uuid", fileId);
    }

    /**
     * @param jsonParam Json
     * @功能描述 上传为json文件
     */
    @PostMapping(value = "/run")
    @ResponseBody
    public synchronized R run(HttpServletRequest request, @RequestParam("jsonParam") String jsonParam, @RequestPart(value = "graph", required = false) MultipartFile graph, @RequestPart(value = "feature", required = false) MultipartFile feature, @RequestPart(value = "label", required = false) MultipartFile label) {
        log.info("运行算法中......参数为 : {}", JSONUtil.toJsonStr(jsonParam));
        // 校验json格式
        if (!JSONUtil.isJson(jsonParam)) {
            return R.error(BizCodeEnum.USER_ERROR_A0421.getCode(), BizCodeEnum.USER_ERROR_A0421.getMsg());
        }

        // 文件名
        String fileId = FormatUtils.getUUID();

        try {
            //判断文件是否为空，不为空时，保存文件
            if (!graph.isEmpty()) {
                log.info("graph不为空");
                // 校验文件名
                if (!FileUtils.isValidFilename(graph.getName())) {
                    return R.error(BizCodeEnum.USER_ERROR_A0701.getCode(), BizCodeEnum.USER_ERROR_A0701.getMsg());
                }

                // 校验文件格式
                if (!FileTypeUtils.checkAlgorithmType(graph.getOriginalFilename())) {
                    return R.error(BizCodeEnum.USER_ERROR_A0701.getCode(), BizCodeEnum.USER_ERROR_A0701.getMsg());
                }
                saveFile(graph, graphDir, fileId);
            }

            //判断文件是否为空，不为空时，保存文件
            if (!feature.isEmpty()) {
                log.info("feature不为空");
                // 校验文件名
                if (!FileUtils.isValidFilename(feature.getName())) {
                    return R.error(BizCodeEnum.USER_ERROR_A0701.getCode(), BizCodeEnum.USER_ERROR_A0701.getMsg());
                }

                // 校验文件格式
                if (!FileTypeUtils.checkAlgorithmType(feature.getOriginalFilename())) {
                    return R.error(BizCodeEnum.USER_ERROR_A0701.getCode(), BizCodeEnum.USER_ERROR_A0701.getMsg());
                }
                saveFile(feature, featureDir, fileId);
            }

            //判断文件是否为空，不为空时，保存文件
            if (!label.isEmpty()) {
                log.info("label不为空");
                // 校验文件名
                if (!FileUtils.isValidFilename(label.getName())) {
                    return R.error(BizCodeEnum.USER_ERROR_A0701.getCode(), BizCodeEnum.USER_ERROR_A0701.getMsg());
                }

                // 校验文件格式
                if (!FileTypeUtils.checkAlgorithmType(label.getOriginalFilename())) {
                    return R.error(BizCodeEnum.USER_ERROR_A0701.getCode(), BizCodeEnum.USER_ERROR_A0701.getMsg());
                }
                saveFile(label, labelDir, fileId);
            }
        } catch (NullPointerException | IOException e) {
            log.error("运行算法异常错误为:{}", ErrorUtils.errorInfoToString(e));
        }

        // 去除转义
        AlgorithmTaskDTO algorithmTaskDTO = JSONUtil.toBean(jsonParam, AlgorithmTaskDTO.class);
        algorithmTaskDTO.setTaskId(fileId);

        try {
            converMessage(algorithmTaskDTO);
        } catch (AmqpException amqpException) {
            log.error("发送消息失败:{}", ErrorUtils.errorInfoToString(amqpException));
            return R.error(BizCodeEnum.SERVICE_ERROR_C0121.getCode(), BizCodeEnum.SERVICE_ERROR_C0121.getMsg());
        }

        Date date = DateUtil.date();

        // 写入任务
        log.info("写入任务");
        TaskEntity taskEntity = new TaskEntity();
        taskEntity.setCreateTime(date);
        taskEntity.setId(fileId);
        taskEntity.setStatus(StatusEnum.NEW.getValue());
        taskEntity.setUserId("1");
        taskEntity.setUsername("yuxingzh");
        taskEntity.setTaskSn(algorithmTaskDTO.getTaskType());
        taskService.save(taskEntity);

        return R.ok().put("uuid", fileId);
    }

    /**
     * 保存文件到指定位置
     *
     * @param originalFile 需要上传的文件
     * @param path         文件路径
     * @param fileName     文件名
     * @throws IOException
     */
    public void saveFile(MultipartFile originalFile, String path, String fileName) throws IOException {
        //获取文件名
        log.info("保存文件:{}", path + "/" + fileName);

        String fileSuffix = originalFile.getOriginalFilename().substring(originalFile.getOriginalFilename().lastIndexOf("."));

        try {
            FileUtil.writeBytes(originalFile.getBytes(), path + "/" + fileName + fileSuffix);
//            originalFile.transferTo(file);
        } catch (NullPointerException | IOException e) {
            log.error("运行算法异常错误为:{}", ErrorUtils.errorInfoToString(e));
        }
    }

    /**
     * @param algorithmTaskDTO
     */
    public void converMessage(AlgorithmTaskDTO algorithmTaskDTO) throws AmqpException {
        String s = EscapeUtil.unescapeHtml4(JSONUtil.toJsonStr(algorithmTaskDTO));
        log.info("发送数据:{}", s);
        rabbitTemplate.convertAndSend("algorithmFanoutExchange", "", s);
    }

    /**
     *
     */
    @PostMapping("conver")
    public void converMessage() throws AmqpException {
        RabbitMqResponseDTO rabbitMqResponseDTO = new RabbitMqResponseDTO();
        rabbitMqResponseDTO.setId("ada00af824ce4913af59619cc422f317");
        rabbitMqResponseDTO.setStatus(2);
        String s = JSONUtil.toJsonStr(rabbitMqResponseDTO);
        log.info("发送数据:{}", s);
        rabbitTemplate.convertAndSend("algorithmFanoutExchangeAck", "", s);
    }


}
